#include "datacenter.h"

#include <QStandardPaths>
#include <QDir>
#include <QJsonObject>
#include <QJsonDocument>

///
/// \brief model::DataCenter::instance 单例指针
///
model::DataCenter* model::DataCenter::instance = nullptr;

///
/// \brief model::DataCenter::getInstance 获取单例
/// \return DataCenter单例指针
///
model::DataCenter *model::DataCenter::getInstance()
{
    if (instance == nullptr) {
        instance = new DataCenter();
    }
    return instance;
}

///
/// \brief model::DataCenter::DataCenter DataCenter实例构造方法
///
model::DataCenter::DataCenter()
{
    // 此处只是把这几个 hash 类型的属性 new 出实例. 其他的 QList 类型的属性, 都暂时不实例化.
    // 主要是为了使用 nullptr 表示 "非法状态"
    // 对于 hash 来说, 不关心整个 QHash 是否是 nullptr, 而是关心, 某个 key 对应的 value 是否存在~~
    // 通过 key 是否存在, 也能表示该值是否有效.
    recentMessages = new QHash<QString, QList<Message>>();
    memberList = new QHash<QString, QList<UserInfo>>();
    unreadMessageCount = new QHash<QString, int>();

    // 加载数据
    loadDataFile();
}

///
/// \brief model::DataCenter::~DataCenter DataCenter实例析构方法
///
model::DataCenter::~DataCenter()
{
    // 释放所有的成员
    // 此处不必判定 nullptr, 直接 delete 即可!
    // C++ 标准中明确规定, 针对 nullptr 进行 delete, 是合法行为, 不会有任何副作用.
    delete myself;
    delete friendList;
    delete chatSessionList;
    delete memberList;
    delete applyList;
    delete recentMessages;
    delete unreadMessageCount;
    delete searchUserResult;
    delete searchMessageResult;
}

///
/// \brief model::DataCenter::initDataFile 数据初始化
///
void model::DataCenter::initDataFile()
{
    // 构造程序数据存储路径：C:\\Users\\UserName\\AppData\\Roamming\\ChatClient\\ChatClient.json
    QString basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QString filePath = basePath + "/ChatClient.json";
    LOG() << "filePath=" << filePath;

    // 检测数据存放目录是否存在，不存在就创建
    QDir dir;
    if (!dir.exists(basePath))
    {
        dir.mkpath(basePath);
    }

    // 构造好文件路径之后, 把文件创建出来.
    // 写方式打开, 并且写入初始内容
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        LOG() << "打开文件失败!" << file.errorString();
        return;
    }
    // 打开成功, 写入初始内容.
    QString data = "{\n\n}";
    file.write(data.toUtf8());

    // 写完之后一定要记得关闭文件
    file.close();
}

///
/// \brief model::DataCenter::saveDataFile 数据持久化
///
void model::DataCenter::saveDataFile()
{
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/ChatClient.json";

    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        LOG() << "文件打开失败! " << file.errorString();
        return;
    }

    // 按照 json 格式来写入数据.
    // 这个对象就可以当做 map 一样来使用.
    QJsonObject jsonObj;
    jsonObj["loginSessionId"] = loginSessionId;

    QJsonObject jsonUnread;
    for (auto it = unreadMessageCount->begin(); it != unreadMessageCount->end(); ++it)
    {
        // 注意 Qt 的迭代器使用细节和 STL 略有差别. 此处不是使用 first / second 的方式
        jsonUnread[it.key()] = it.value();
    }
    jsonObj["unread"] = jsonUnread;

    // 把 json 写入文件了
    QJsonDocument jsonDoc(jsonObj);
    QString s = jsonDoc.toJson();
    file.write(s.toUtf8());

    // 关闭文件
    file.close();
}

///
/// \brief model::DataCenter::loadDataFile 从数据文件中加载数据到内存
///
void model::DataCenter::loadDataFile()
{
    // 确保在加载之前, 先针对文件进行初始化操作.
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/ChatClient.json";

    // 判定文件是否存在, 不存在则初始化, 并创建出新的空白的 json 文件
    QFileInfo fileInfo(filePath);
    if (!fileInfo.exists())
    {
        initDataFile();
    }

    // 读方式打开文件
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        LOG() << "打开文件失败! " << file.errorString();
        return;
    }

    // 读取到文件内容, 解析为 JSON 对象
    QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll());
    if (jsonDoc.isNull())
    {
        LOG() << "解析 JSON 文件失败! JSON 文件格式有错误!";
        file.close();
        return;
    }

    // 从文件加载登录会话ID
    QJsonObject jsonObj = jsonDoc.object();
    this->loginSessionId = jsonObj["loginSessionId"].toString();
    LOG() << "loginSessionId=" << this->loginSessionId;

    // 从文件加载会话列表及其未读信息条数
    this->unreadMessageCount->clear();
    QJsonObject jsonUnread = jsonObj["unread"].toObject();
    for (auto it = jsonUnread.begin(); it != jsonUnread.end(); ++it)
    {
        this->unreadMessageCount->insert(it.key(), it.value().toInt());
    }

    // 完成数据加载后一定要关闭文件
    file.close();
}














